
#pragma once

#include "tiffiop.h"

using namespace System;
using namespace System::Collections::Generic;
using namespace System::IO;

using namespace Derm;
using namespace Derm::Raster;

namespace Derm {

	/// <summary>
	/// Managed plugin wrapping external libraries.
	/// </summary>
	ref class TiffImageCodecPlugin : ImageCodecPlugin
	{
#pragma region TIFF Format Management

	private:

		static Pixel::Type GetPixelTypeFromTiff(TIFF *tif, ImageCodec::CodecCriteria ^criteria);

		/// <summary>
		/// Load a generic TIFF using the RGBA interface.
		/// </summary>
		/// <remarks>
		/// This routine expect that image pixel type is Pixel::Type::RGB24 or Pixel::Type::RGBA32.
		/// This routine can be used for loading exotic TIFF image formats.
		/// </remarks>
		static void LoadGenericRgbaTiff(TIFF *tif, Image ^image);

		static void LoadFloatingPrecisionTiff(TIFF *tif, Image ^image);

		static void LoadLogarithmicTiff(TIFF *tif, Image ^image);

		static void ConvertLUV2RGB(float *src, float *dst, double stonits);

		/// <summary>
		/// Load a generic stripped/tiled TIFF.
		/// </summary>
		/// <remarks>
		/// This routine expect that image memory layout is corresponding exactly to the TIFF format.
		/// </remarks>
		static void LoadGenericTiff(TIFF *tif, Image ^image);

		/// <summary>
		/// Load a generic stripped TIFF.
		/// </summary>
		/// <remarks>
		/// This routine supports both PLANARCONFIG_CONTIG and PLANARCONFIG_SEPARATE tags.
		/// This routine expect that image memory layout is corresponding exactly to the TIFF strip format.
		/// </remarks>
		static void LoadGenericStripTiff(TIFF *tif, Image ^image);

		/// <summary>
		/// Load a generic tiled TIFF.
		/// </summary>
		/// <remarks>
		/// This routine supports only PLANARCONFIG_CONTIG tag.
		/// This routine expect that image memory layout is corresponding exactly to the TIFF tile format.
		/// </remarks>
		static void LoadGenericTileTiff(TIFF *tif, Image ^image);

		/// <summary>
		/// Determine whether a TIFF has an associated alpha component.
		/// </summary>
		static bool HasAssociatedAlpha(TIFF *tif);

		/// <summary>
		/// Determine whether a TIFF has an associated alpha component.
		/// </summary>
		static bool HasAssociatedAlpha(TIFF *tif, int &extraChannels);

		/// <summary>
		/// Determine the useful samples per pixel (i.e. not the extra channels except the associated alpha).
		/// </summary>
		static uint16 GetRealSamplesPerPixel(TIFF *tif);

		/// <summary>
		/// Determine the useful samples per pixel (i.e. not the extra channels except the associated alpha).
		/// </summary>
		static uint16 GetRealSamplesPerPixel(TIFF *tif, int &extraChannels);

		static void PostDivideAlpha(TIFF *tif, Image ^image);

		/// <summary>
		/// Orientate image to follow TIFF format tags.
		/// </summary>
		static void OrientateTiff(TIFF *tif, Image ^image);

#pragma endregion

#pragma region IImageCodecPlugin Implementation

	public:

		/// <summary>
		/// Determine whether this plugin is available for the current process.
		/// </summary>
		/// <returns>
		/// It returns a boolean value indicating whether the plugin is available for the current
		/// process.
		/// </returns>
		virtual bool CheckAvailability() override;

		/// <summary>
		/// Plugin name.
		/// </summary>
		/// <remarks>
		/// This name identity the plugin implementation. It shall be unique in the plugin set available at
		/// runtime.
		/// </remarks>
		virtual property String ^Name
		{
			String ^ get() override {
				return (gcnew String("Derm.TIFF"));
			}
		}

		/// <summary>
		/// Query image file informations.
		/// </summary>
		/// <returns>
		/// A <see cref="ImageInfo"/>
		/// </returns>
		virtual ImageInfo ^QueryImageInfo(Stream ^fs, ImageCodec::CodecCriteria ^criteria) override;

		/// <summary>
		/// Load image from file.
		/// </summary>
		/// <param name="fs">
		/// A <see cref="FileStream"/> where the image data is stored.
		/// </param>
		/// <param name="criteria">
		/// A <see cref="ImageCodec.CodecCriteria"/> specifying parameters for loading an image stream.
		/// </param>
		/// <returns>
		/// An <see cref="Image"/> holding the image data.
		/// </returns>
		virtual Image ^Load(Stream ^fs, ImageCodec::CodecCriteria ^criteria) override;

		/// <summary>
		/// Save image pixels to file.
		/// </summary>
		/// <param name="fs">
		/// A <see cref="System.IO.FileStream"/> which stores the image data.
		/// </param>
		/// <param name="image">
		/// A <see cref="Image"/> holding the data to be stored.
		/// </param>
		/// <param name="criteria">
		/// A <see cref="ImageCodec.CodecCriteria"/> specifying parameters for loading an image stream.
		/// </param>
		virtual void Save(Stream ^fs, Image ^image, ImageFormat format, ImageCodec::CodecCriteria ^criteria) override;

		/// <summary>
		/// Get a list of image formats supported for reading.
		/// </summary>
		virtual property IEnumerable<ImageFormat> ^SupportedReadImageFormats
		{
			IEnumerable<ImageFormat> ^get() override {
				List<ImageFormat> ^formats = gcnew List<ImageFormat>();

				formats->Add(ImageFormat::Tiff);

				return (formats);
			}
		}

		/// <summary>
		/// Get a list of image formats supported for writing.
		/// </summary>
		virtual property IEnumerable<ImageFormat> ^SupportedWriteImageFormats
		{
			IEnumerable<ImageFormat> ^get() override {
				return (gcnew List<ImageFormat>());
			}
		}

#pragma endregion
	};

}
